/**
* Copyright © 2017-2018, by 晓叹星沉.
*/
package org.aurora.dao.impl;

import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;

import javax.persistence.Entity;

import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.beanutils.Converter;
import org.apache.commons.lang3.StringUtils;
import org.aurora.common.model.Page;
import org.aurora.common.model.PageBean;
import org.aurora.dao.IBaseDAO;
import org.aurora.utils.bean.BeanUtils;
import org.hibernate.Hibernate;
import org.hibernate.SQLQuery;
import org.hibernate.ScrollMode;
import org.hibernate.ScrollableResults;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.query.NativeQuery;
import org.hibernate.query.Query;
import org.hibernate.transform.Transformers;
import org.hibernate.type.Type;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

/**
 * 
 * <p>
 * 数据库访问实现类
 * </p>
 * @author 晓叹星沉
 * @since jdk1.8
 * 2018年6月2日
 *  
 */
@Transactional
@Repository
public class BaseDao implements IBaseDAO {

	@Autowired
	private SessionFactory sessionFactory;

    public Session getSession(){
        return sessionFactory.getCurrentSession();
    }
	/** 
	 * {@inheritDoc}   
	 * @see org.aurora.dao.IBaseDAO#save(java.lang.Object) 
	 */
	@Override
	public String save(Object po) {
		Serializable pk = getSession().save(po);
		return pk == null ? null : pk.toString();
	}

	public SessionFactory getSessionFactory() {
        return sessionFactory;
    }
    
    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }
	/** 
	  * {@inheritDoc}   
	  * @see org.aurora.dao.IBaseDAO#get(java.lang.Class, java.io.Serializable) 
	  */
	@SuppressWarnings("unchecked")
	@Override
	public <T> T get(Class<T> clazz, Serializable id) {
		return (T)getSession().get(clazz, id);
	}
	/** 
	  * {@inheritDoc}   
	  * @see org.aurora.dao.IBaseDAO#delete(java.lang.Object) 
	  */
	@Override
	public void delete(Object po) {
		getSession().delete(po);
	}
	/** 
	  * {@inheritDoc}   
	  * @see org.aurora.dao.IBaseDAO#update(java.lang.Object) 
	  */
	@Override
	public void update(Object po) {
		getSession().update(po);
	}
	/** 
	  * {@inheritDoc}   
	  * @see org.aurora.dao.IBaseDAO#queryListBySql(java.lang.Class, java.lang.String) 
	  */
	@SuppressWarnings("unchecked")
	@Override
	public <T> List<T> queryListBySql(String sql, Map<String, ?> params, Class<T> resultObj) {
		NativeQuery<T> query = createSqlQuery(sql, params);
		if(null != resultObj){
			//hibernate映射对象直接返回
			if(resultObj.isAnnotationPresent(Entity.class)){
				query.addEntity(resultObj);
				return query.list();
			}else{//非映射对象，则查询出结果按变量变成赋值返回
				List<T> list = query.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP).list();
				if(CollectionUtils.isEmpty(list)) {
					return list;
				}else{
					 return covertResult2Obj(resultObj, list);
				}
			}
		}
		return query.list();
		
	}
	/**
	 * 常见sql query
	 * @param sql
	 * @param params
	 * @return
	 */
	private <T> NativeQuery<T> createSqlQuery(String sql, Map<String, ?> params) {
		NativeQuery<T> query = getSession().createSQLQuery(sql);
		processQueryParameters(query, params);
		return query;
	}
	/**
	 * 将查询结果转化为对象
	 * @param resultObj
	 * @param results
	 * @param list
	 */
	private <T> List<T> covertResult2Obj(Class<T> resultObj, List<T> list){
		List<T> results = new ArrayList<T>();
		for (T t : list) {
			T obj = null;
			try {
				obj = resultObj.newInstance();
				Map<String, Object> map = (Map<String, Object>) t;
				Set<String> set = map.keySet();
				for (String name : set) {
					String filedName = this.hungary2CamelCase(name);
					BeanUtils.setProperty(obj, filedName, map.get(name));
				}
			} catch (Exception e) {
				throw new RuntimeException(e.getMessage());
			}
			results.add(obj);
		}
		return results;
	}
	
	/**
	 * 
	 * 将匈牙利命名法（即AA_BB）转为驼峰命名法
	 * @param str
	 * @return
	 */
	private String hungary2CamelCase(String str){
		StringBuilder sb = new StringBuilder();
		if(StringUtils.isNotBlank(str)){
			StringTokenizer strToken = new StringTokenizer(str,"_");
			while(strToken.hasMoreTokens()){
				String word = strToken.nextToken().toLowerCase();
				if(!StringUtils.isBlank(sb)){
					word = (new StringBuilder()).append(Character.toUpperCase(word.charAt(0))).append(word.substring(1)).toString();
				}
				sb.append(word);
			}
		}
        return sb.toString();
	}
	/** 
	  * {@inheritDoc}   
	  * @see org.aurora.dao.IBaseDAO#queryListByHql(java.lang.String, java.util.Map) 
	  */
	@Override
	public <T> List<T> queryListByHql(String hql, Map<String, ?> params) {
		Query<T> query = createHqlQuery(hql, params);
		return query.list();
	}
	
	@SuppressWarnings("unchecked")
	private <T> Query<T> createHqlQuery(String hql, Map<String, ?> params){
		Query<T> query = getSession().createQuery(hql);
		processQueryParameters(query, params);
		return query;
	}
	
	/**
     * 为 Hibernate 的查询对象设置查询条件的参数
     * @param query Hibernate 查询对象
     * @param params 包含查询条件参数映射关系的 Map
     */
    private void processQueryParameters(Query query, Map<String, ?> params) {
        Set<String> paramNames = query.getParameterMetadata().getNamedParameterNames();
        if(paramNames.isEmpty()){
        	return;
        }

        // 设置查询对象的参数
        for (String name : paramNames) {
            if (!params.containsKey(name)) {
                throw new RuntimeException("没有设置参数 [" + name + "] 的值， 查询语句为： " + query.getQueryString());
            }

            Object value = params.get(name);
            if (value instanceof String[]) {
                String[] arrayVal = (String[]) value;
                if (arrayVal.length == 0) {
                    value = "";
                } else if (arrayVal.length == 1) {
                    value = arrayVal[0];
                }
            }
            
            if (value instanceof Collection) {
                query.setParameterList(name, (Collection<?>) value);
            } else if (value instanceof Object[]) {
                query.setParameterList(name, (Object[]) value);
            } else {
                query.setParameter(name, value);
            }
        }
    }
	/** 
	  * {@inheritDoc}   
	  * @see org.aurora.dao.IBaseDAO#queryPageByHql(java.lang.String, java.util.Map, int, int) 
	  */
	@Override
	public <T, V> Page<T, V> queryPageByHql(String hql, Map<String, ?> params, int page, int pageSize) {
		Query<T> query = createHqlQuery(hql, params);
		
		PageBean pageBean = initPageBean(page, pageSize, query);
		
		List<T> list = null;
		if(pageBean.getTotal() == 0){
			list = Collections.emptyList();
		}else{
			if(pageBean.getPageSize() != -1){
				query.setFirstResult(pageBean.getStartNo() - 1);
				if (pageBean.getPageSize() != Integer.MAX_VALUE) { // 进行分页查询
					query.setMaxResults(pageBean.getPageSize());
				}
			}
            list = query.list();
		}
		
		return new Page<T, V>(pageBean, list);
	}
	
	/**
	 * 查询记录总数
	 * @param query
	 */
	private int getCount(Query query) {
		// 以游标的方式获取符合查询条件的总记录数
        ScrollableResults scrollableResults = query.scroll(ScrollMode.SCROLL_SENSITIVE);
        scrollableResults.last();
        // getRowNumber() 的返回值从 0 开始，查询结果为空时为 -1， 所以返回总记录条数需要加上 1
        int count = scrollableResults.getRowNumber() + 1;
        scrollableResults.close();
        return count;
	}
	/** 
	  * {@inheritDoc}   
	  * @see org.aurora.dao.IBaseDAO#queryListBySql(java.lang.String, java.util.Map, java.lang.Class, int, int) 
	  */
	@Override
	public <T, V> Page<T, V> queryPageBySql(String sql, Map<String, ?> params, Class<T> resultObj, int page, int pageSize) {
		NativeQuery<T> query = getSession().createSQLQuery(sql);
		processQueryParameters(query, params);
		
		PageBean pageBean = initPageBean(page, pageSize, query);
		
		List<T> list = null;
		if(pageBean.getTotal() == 0){
			list = Collections.emptyList();
		}else{
			query.setFirstResult(pageBean.getStartNo() - 1);
            if (pageBean.getPageSize() != Integer.MAX_VALUE) { // 进行分页查询
                query.setMaxResults(pageBean.getPageSize());
            }
            if(null != resultObj){
    			//hibernate映射对象直接返回
    			if(resultObj.isAnnotationPresent(Entity.class)){
    				query.addEntity(resultObj);
    				list = query.list();
    			}else{//非映射对象，则查询出结果按变量变成赋值返回
    				list = query.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP).list();
    				if(!CollectionUtils.isEmpty(list)) {
    					 covertResult2Obj(resultObj, list);
    				}
    			}
    		}
		}
		
		return new Page<T, V>(pageBean, list);
	}
	/**
	 * 初始化pageBean信息
	 * @param page
	 * @param pageSize
	 * @param query
	 * @return
	 */
	private <T> PageBean initPageBean(int page, int pageSize, Query query) {
		int count = getCount(query);
		int totalPage = (int)Math.ceil((double) count / pageSize); 
		if(page > totalPage){
			page = totalPage;
		}else if(page <= 0){
			page = 1;
		}
		PageBean pageBean = new PageBean(page, pageSize);
		pageBean.setTotal(count);
		return pageBean;
	}
	/** 
	  * {@inheritDoc}   
	  * @see org.aurora.dao.IBaseDAO#executeUpdateSql(java.lang.String, java.util.Map) 
	  */
	@SuppressWarnings("rawtypes")
	@Override
	public int executeUpdateSql(String sql, Map<String, ?> params) {
		if(StringUtils.isBlank(sql)) {
            throw new IllegalArgumentException("SQL 语句不能为空");
        }
        
        if(sql.matches("(?i)\\s*select\\b.+")) {
            throw new IllegalArgumentException("不支持执行查询[SELECT] SQL 语句");
        }
		NativeQuery query = createSqlQuery(sql, params);
		return query.executeUpdate();
	}
	
	
}
